home *** CD-ROM | disk | FTP | other *** search
- /* KBFAKE.C
-
- This program is a Terminate and Stay Resident program that will
- redirect the input from one serial line so that it looks like it has
- been typed on the keyboard. It works by installing an interrupt handler
- for the serial port that is to be used. This handler inserts the
- characters coming in over the serial line into the keyboard buffer using
- the BIOS keyboard write call.
- The code for this program is a mixture of the example for the
- keep() function call in Borland C and various programming examples from
- the SIMTEL internet FTP site.
- This is an interrupt service routine. You can NOT compile this
- program with Test Stack Overflow turned on and get an executable file
- which will operate correctly. */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <bios.h> // bioscom() declared here
- #include <dos.h> // keep() is declared here
-
- // Reduce heaplength and stacklength to make a smaller program in memory
- extern unsigned _heaplen = 256;
- extern unsigned _stklen = 1024;
-
- const unsigned BIOSdseg = 0x0040; // BIOS data segment
- const unsigned IMR = 0x21; // Interrupt Mask Register
- const unsigned safety_space = 1024; // Bytes of spare space
-
- // These are offsets from the UART base port
- const unsigned UART_THR = 0x00; // Transmit Hold Register and
- const unsigned UART_RBR = 0x00; // Receiver Buffer Register
- const unsigned UART_IER = 0x01; // Interrupt Enable Register
- const unsigned UART_IIR = 0x02;
- const unsigned UART_LCR = 0x03; // Line Control Register?
- const unsigned UART_MCR = 0x04; // Modem Control Register?
- const unsigned UART_LSR = 0x05; // Line Status Register
- const unsigned UART_MSR = 0x06;
-
- //-----------------------------------------------------------------
- // Global variables needed both during initialization and interrupt
- // processing.
- //-----------------------------------------------------------------
-
- int base_port; // Base I/O port address
-
-
- //-----------------------------------------------------------------
- // Handle the incoming data interrupt from the serial port.
- // Read the incoming character and add it to the BIOS keyboard
- // buffer.
- // Note: Interrupts are disabled by the processor as part of
- // the interrupt processing, so we do not need to disable them by
- // hand. We also don't need to enable interrupts before leaving,
- // since the FLAGS register is popped from the stack upon IRET,
- // restoring the interrupt control flag.
- //-----------------------------------------------------------------
-
- void interrupt serial_input_handler(void) {
- static unsigned char in_char;
- static unsigned char scancode;
-
- // Get the character from the comm port RBR register
- in_char = inportb(base_port + UART_RBR);
-
- // Set the keyboard scan code to zero, which is what would
- // happen if they had used the alt-numpad method to enter
- // the character, rather than pressing the key. This should
- // not be noticed by keyboard-reading software, and saves
- // having to keep a table of key scan codes for each ASCII
- // code.
- scancode = 0;
-
- // Write the character to the keyboard buffer using BIOS
- // Use BIOS call 0x16 (Keyboard services)
- // AH = service to use = 0x05 (keyboard write)
- // CL = ASCII code of character
- // CH = Keyboard scan code of character
- _AH = 0x05;
- _CL = in_char;
- _CH = scancode;
- geninterrupt(0x16);
-
- // Tell the 8259A End Of Interrupt
- outportb(0x20,0x20);
- }
-
- void Usage(char *s) {
- fprintf(stderr,"Usage: %s [port]\n",s);
- fprintf(stderr," port: Serial COMM port to read (1-4)\n");
- fprintf(stderr," (default = 1)\n");
- exit(-1);
- }
-
- void main(unsigned argc, char *argv[]) {
- int port = 1; // COMM port to read (1-4)
- int temp; // Temporary holding register
- int int_to_grab; // Which interrupt vector to grab
- int int_to_enable; // Which hardware interrupt to enable
-
- // Parse the command line
- switch (argc) {
- case 1: // No arguments
- break;
- case 2: // COM port number (1-4) as argument
- port = atoi(argv[1]);
- if ( (port < 1) || (port > 4) ) Usage(argv[0]);
- break;
- default:
- Usage(argv[0]);
- }
-
- // Find the interrupt to use based on the port selected
- // For some reason, the interrupt you grab the vector for is
- // not the same as the hardware interrupt you enable.
- switch (port) {
- case 1:
- case 3:
- int_to_grab = 0x0c;
- int_to_enable = 4;
- break;
- case 2:
- case 4:
- int_to_grab = 0x0b;
- int_to_enable = 3;
- break;
- default:
- fprintf(stderr,"Internal error 1 - port is %d\n",port);
- exit(-1);
- }
-
- // Find the I/O base address based on the port selected
- // This information is read from the BIOS data segment that
- // is at segment 0x0040. The first four words hold the
- // I/O base addresses for the first four com ports.
- base_port = *(int far *)MK_FP(BIOSdseg,(port-1)*2);
- if (base_port == 0) {
- fprintf(stderr,"COM%d has no BIOS base port\n",port);
- fprintf(stderr," (KBFAKE not installed)\n");
- exit(-1);
- }
-
- // Tell what we're doing
- printf("KBFAKE: Installing on port %d (vector %2X, interrupt %d, I/O base %4X)\n",
- port, int_to_grab, int_to_enable, base_port);
-
- // Set the parameters on the COM port to:
- // 0xE0: 9600 Baud
- // 0x03: 8 Data bits (0x02 = 7 data bits)
- // 0x00: 1 Stop bit (0x04 = 2 stop bits)
- // 0x00: No parity (0x08 = odd, 0x18 = even)
- (void)bioscom(0, 0xE0 | 0x03 | 0x00 | 0x00 , port-1);
- printf(" 9600 baud, 8 data bits, 1 stop bit, no parity\n");
-
- /* install the new interrupt handler for serial input */
- setvect(int_to_grab, serial_input_handler);
-
- // Disable interrupts while we are messing with the UART
- disable();
-
- //-----------------------------------------------------------------
- // Program the UART for the serial port we are using
- //-----------------------------------------------------------------
-
- // Turn off the Divisor Access Latch Bit to allow access to RBR, etc.
- temp = inportb(base_port + UART_LCR) & 0x7F; // Strip upper bit
- outportb(base_port + UART_LCR, temp);
-
- // Assert DTR, RTS, and OUT2 on the COM port
- outportb(base_port + UART_MCR, 0x0B);
-
- // Enable interrupts for data ready on the serial line (8250)
- outportb(base_port + UART_IER, 0x01);
-
- // Read the Line Status Register to clear any reported errors
- temp = inportb(base_port + UART_LSR);
-
- // Read the Receiver Buffer Register to clear any incoming character
- temp = inportb(base_port + UART_RBR);
-
- //-----------------------------------------------------------------
- // Enable the IRQ for the serial line on the 8259A interrupt control
- // Do this by clearing the bit in the ISR corresponding to the
- // interrupt that is to be enabled.
- //-----------------------------------------------------------------
-
- temp = inportb(IMR); // Read the value of the Interrupt Mask Reg
- temp &= ~(1 << int_to_enable); // Zero bit for one to enable
- outportb(IMR,temp); // Send the value back
-
- // Re-enable interrupts, since all is set up
- enable();
-
- // Terminate and Stay Resident
- /* _psp is the starting address of the
- program in memory. The top of the stack
- is the end of the program. Using _SS and
- _SP together we can get the end of the
- stack. You may want to allow a bit of
- safety space to insure that enough room
- is being allocated ie:
- (_SS + ((_SP + safety space)/16) - _psp)
- */
- keep(0,(_SS+( (_SP+safety_space)/16)-_psp));
- }
-